{
 "cells": [
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# nonplanar spacer\n",
    "\n",
    "*<<< check out other demo models [here](https://github.com/FullControlXYZ/fullcontrol/tree/master/models/README.md) >>>*\n",
    "  \n",
    "press ctrl+F9 to run all cells in this notebook, or press shift+enter to run each cell sequentially \n",
    "\n",
    "if you change one of the code cells, make sure you run it and all subsequent cells again (in order)\n",
    "\n",
    "*this document is a jupyter notebook - if they're new to you, check out how they work: [link](https://www.google.com/search?q=ipynb+tutorial), [link](https://jupyter.org/try-jupyter/retro/notebooks/?path=notebooks/Intro.ipynb), [link](https://colab.research.google.com/)*\n### be patient :)\n\nthe next code cell may take a while because running it causes several things to happen:\n- connect to a google colab server -> download the fullcontrol code -> install the fullcontrol code\n\ncheck out [other tutorials](https://github.com/FullControlXYZ/fullcontrol/blob/master/tutorials/README.md) to understand the python code for the FullControl design"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "if 'google.colab' in str(get_ipython()):\n  !pip install git+https://github.com/FullControlXYZ/fullcontrol --quiet\nimport fullcontrol as fc\nfrom google.colab import files\n",
    "from math import cos, tau"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# printer/gcode parameters\n",
    "\n",
    "design_name = 'nonplanar_spacer'\n",
    "nozzle_temp = 210\n",
    "bed_temp = 40\n",
    "print_speed = 500\n",
    "fan_percent = 100\n",
    "printer_name='prusa_i3' # generic / ultimaker2plus / prusa_i3 / ender_3 / cr_10 / bambulab_x1 / toolchanger_T0"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# design parameters\n",
    "\n",
    "# main design parameters\n",
    "waves = 6\n",
    "# Waves - Number of waves around the circumference\n",
    "# default value: 6 ; guideline range: 1 to 10\n",
    "\n",
    "total_thickness = 4\n",
    "# Thickness (mm) - Total thickness of the spacer\n",
    "# default value: 4 ; guideline range: 0 to 10\n",
    "\n",
    "D1 = 8\n",
    "# Hole size (mm) - Diameter of inner hole\n",
    "# default value: 8 ; guideline range: 4 to 10\n",
    "\n",
    "\n",
    "# advanced parameters\n",
    "\n",
    "D_ratio = 3\n",
    "# Diameter ratio - Outer diameter is this value multiplied by hole size\n",
    "# default value: 3 ; guideline range: 1.5 to 5\n",
    "\n",
    "material_thickness = 0.4\n",
    "# Material thickness (mm) - Material thickness - recommended to be set equal to nozzle diameter - akin to conventional 'layer height' at the bottom of waves but may be more akin to conventional 'line width' at the top of waves\n",
    "# default value: 0.4 ; guideline range: 0.2 to 2\n",
    "\n",
    "EW_EH_ratio = 2\n",
    "# Extrudate aspect ratio - Ratio of extrusion width to extrusion height - a value of 2 is recommended for this structure\n",
    "# default value: 2 ; guideline range: 1 to 4\n",
    "\n",
    "overlap_percent = 20\n",
    "# Extrudate overlap % - Lateral overlap between adjacent extrudates - defined as a percentage of extrudate width - recommended value 10 to 20\n",
    "# default value: 20 ; guideline range: 0 to 40\n",
    "\n",
    "contraction_factor = 1.2\n",
    "# Wave contraction factor - Moves wave tips inwards... good for polymer squish. If set too low, structure may collapse. If set too high, the nozzle may scrape previous layer. Recommended value 1 to 1.2, maybe more for shallower structures.\n",
    "# default value: 1.2 ; guideline range: 0 to 2\n",
    "\n",
    "quantity = 1\n",
    "# Quantity - Number of parts to print\n",
    "# default value: 1 ; guideline range: 1 to 5\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# create design steps\n",
    "\n",
    "EH = material_thickness\n",
    "EW = EH*EW_EH_ratio\n",
    "D2 = D1*D_ratio \n",
    "overlap = (overlap_percent/100) * EW \n",
    "# increase in Z height of nozzle to get desired total_thickness:\n",
    "height = total_thickness - EH \n",
    "R1, R2 = D1/2, D2/2\n",
    "rings = int((R2-R1)/(EW-overlap))\n",
    "segs_per_ring = (waves*2)*int(128/(waves*2)) # this means the segmented path always has a node at the exact min and max of waves\n",
    "\n",
    "centre = fc.Point(x=0, y=0, z=0)\n",
    "# set start point, and travel-line into centre of part:\n",
    "steps = [fc.move_polar(centre, centre, R2, (0.5+((waves+1)%2)/(2*waves))*tau), fc.Extruder(on=False), centre, fc.Extruder(on=True)]\n",
    "# add spiral purge line (if there is space):\n",
    "purge_spiral_passes = min(int((R1-EW)/EW)-1, 3)\n",
    "if purge_spiral_passes > 0: steps.extend(fc.spiralXY(centre, EW/2, R1-EW, 0, purge_spiral_passes, 200))\n",
    "\n",
    "# print part:\n",
    "for ring in range(rings):\n",
    "  for seg in range(segs_per_ring+1):  # need one extra 'seg' to allow for the first segment having a start point as well as an end point\n",
    "    angle_now = (seg/segs_per_ring)*tau\n",
    "    z_now = height*(ring/(rings-1))*(0.5-0.5*cos(angle_now*waves))\n",
    "    radius_now = R1 + EW/2 + ring*(EW-overlap)-(z_now*contraction_factor)\n",
    "    centre.z = z_now\n",
    "    steps.append(fc.polar_to_point(centre, radius_now, angle_now))\n",
    "    \n",
    "# print multiple copies:\n",
    "if quantity > 1: steps = fc.move(steps, fc.Vector(x=R2*2 + 5), copy=True, copy_quantity=quantity)\n",
    "\n",
    "# offset the whole procedure. z dictates the gap between the nozzle and the bed for the first layer, assuming the model was designed with a first layer z-position of 0\n",
    "model_offset = fc.Vector(x=50, y=50, z=0.8*EH)\n",
    "steps = fc.move(steps, model_offset)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# add annotations and plot\n",
    "\n",
    "annotations = []\n",
    "annotations.append(fc.PlotAnnotation(point=fc.midpoint(steps[0],steps[2]), label = \"Initial approach set under a wave-crest to avoid defects\"))\n",
    "annotations.append(fc.PlotAnnotation(point=steps[0], label = \"Start\"))\n",
    "annotations.append(fc.PlotAnnotation(point=steps[-1], label = \"End\"))\n",
    "annotations.append(fc.PlotAnnotation(point=fc.move(steps[2], fc.Vector(z=total_thickness*2)), label=\"A pointy nozzle is best\"))\n",
    "if purge_spiral_passes>0: \n",
    "    annotations.append(fc.PlotAnnotation(point=steps[2], label=\"Spiral flow stabiliser\"))\n",
    "if quantity > 1:\n",
    "    annotations.append(fc.PlotAnnotation(point=fc.move(centre, fc.Vector(x=model_offset.x+(R2*2 + 5),\n",
    "                       y=model_offset.y, z=model_offset.z)), label=\"Designed movement between parts and spiral-purge each time\"))\n",
    "    \n",
    "fc.transform(steps + annotations, 'plot', fc.PlotControls(color_type='print_sequence', initialization_data={'extrusion_width': EW, 'extrusion_height': EH}))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# generate and save gcode\n",
    "\n",
    "gcode_controls = fc.GcodeControls(\n",
    "    printer_name=printer_name,\n",
    "\n",
    "    initialization_data={\n",
    "        'primer': 'front_lines_then_y',\n",
    "        'print_speed': print_speed,\n",
    "        'nozzle_temp': nozzle_temp,\n",
    "        'bed_temp': bed_temp,\n",
    "        'fan_percent': fan_percent,\n",
    "        'extrusion_width': EW,\n",
    "        'extrusion_height': EH})\n",
    "gcode = fc.transform(steps, 'gcode', gcode_controls)\nopen(f'{design_name}.gcode', 'w').write(gcode)\nfiles.download(f'{design_name}.gcode')"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### please tell us what you're doing with FullControl!\n",
    "\n",
    "- tag FullControlXYZ on social media ([twitter](https://twitter.com/FullControlXYZ), [instagram](https://www.instagram.com/fullcontrolxyz/), [linkedin](https://www.linkedin.com/in/andrew-gleadall-068587119/), [tiktok](https://www.tiktok.com/@fullcontrolxyz))\n",
    "- email [info@fullcontrol.xyz](mailto:info@fullcontrol.xyz)\n",
    "- post on the [subreddit](https://reddit.com/r/fullcontrol)\n",
    "- post in the [github discussions or issues tabs](https://github.com/FullControlXYZ/fullcontrol/issues)\n",
    "\n",
    "in publications, please cite the original FullControl paper and the github repo for the new python version:\n",
    "\n",
    "- Gleadall, A. (2021). FullControl GCode Designer: open-source software for unconstrained design in additive manufacturing. Additive Manufacturing, 46, 102109. \n",
    "- Gleadall, A. and Leas, D. (2023). FullControl [electronic resource: python source code]. available at: https://github.com/FullControlXYZ/fullcontrol"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.3"
  },
  "vscode": {
   "interpreter": {
    "hash": "2b13a99eb0d91dd901c683fa32c6210ac0c6779bab056ce7c570b3b366dfe237"
   }
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
